﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CPAIS.Alglorithm;
using CPAIS.Model;
using CPAIS.Updater;

namespace CPAIS.Adapter
{
    public class HLDAGibbsAdapter
    {
        public struct TopicItem
        {
            public struct Item : IComparable<Item>
            {
                public string ActionType;
                public double ActionProb;
                public int ActionCount;

                public int CompareTo(Item other)
                {
                    if (this.ActionProb > other.ActionProb)
                        return -1;
                    else if (this.ActionProb < other.ActionProb)
                        return 1;
                    else
                        return 0;
                }
            }

            public double Prob;
            public string name;
            public Item[] Actions;
        }

        private readonly LogModel _logModel;
        private int _iterations;
        public int Iterations
        {
            get { return _iterations; }
        }
        int _V;
        int _M;
        int _K;
        public int VocabularySize { get { return _V; } }
        public int DocumentSize { get { return _M; } }
        public int TopicSize { get { return _K; } }

        private Dictionary<string, Dictionary<string,double>> docTopicAssoc;
        private TopicItem[] topicWordAssoc;

        public HLDAGibbsAdapter(LogModel logModel)
        {
            _logModel = logModel;
        }
        public Dictionary<string, Dictionary<string, double>> DocTopicAssoc
        {
            get { return docTopicAssoc; }
        }
        public TopicItem[] TopicWordAssoc
        {
            get { return topicWordAssoc; }
        }
        public void Execute(int iterations, ProgressUpdater updater)
        {
            // Records parameters
            _iterations = iterations;

          
            // Gibbs sampling
            CPAIS.Algorithm.HLDAGibbs gibbs = new CPAIS.Algorithm.HLDAGibbs();
            gibbs.ImportLogModel(_logModel);

            try
            {
                if (updater != null)
                    updater.Begin();

                gibbs.StartProcessing(_iterations, updater);

                string szOutput = gibbs.WriteTopicToString();
                _V = gibbs.vocabulary.Count;
                _M = gibbs.documents.Count;
                Algorithm.HLDATopic topic = gibbs.root;
                List<Algorithm.HLDATopic> topics = new List<Algorithm.HLDATopic>();
                topic.GetAllTopics(topics);
                int K = topics.Count;
                _K = K;
                // Gets Document-Topic association

                docTopicAssoc = new Dictionary<string, Dictionary<string, double>>();
                for (int m = 0; m < _M; m++)
                {
                    gibbs.documents[m].CalculateTopicProbability();
              
                    Dictionary<string, double> topicProbs = new Dictionary<string, double>();
                    foreach(var topicNo in gibbs.documents[m].path)
                    {
                        //calculate topic probabilities for each document
                        string szTopicName = topicNo.DisplayName;
                        if (gibbs.documents[m].ContainTopic(szTopicName))
                        {
                            double dProb = gibbs.documents[m].GetTopicProb(szTopicName);
                            if (topicProbs.ContainsKey(szTopicName))
                                topicProbs[szTopicName] = dProb;
                            else
                                topicProbs.Add(szTopicName, dProb);
                        }
                    }
                    docTopicAssoc.Add(gibbs.documents[m].title, topicProbs);
                }

                // Gets Topic-Word association            
                topicWordAssoc = new TopicItem[_K];
              
                for(int k = 0; k < K; k++)
                {
                    Algorithm.HLDATopic curTopic = topics[k];
                    int v = 0;
                    topicWordAssoc[k].name = curTopic.DisplayName;
                    topicWordAssoc[k].Actions = new TopicItem.Item[gibbs.vocabulary.Count];
                    foreach(string szV in gibbs.vocabulary.Keys)
                    {
                        topicWordAssoc[k].Actions[v].ActionType = szV;
                        int nWordIndex = gibbs.vocabulary[szV];
                        topicWordAssoc[k].Actions[v].ActionProb = curTopic.GetWordProb(nWordIndex);
                        topicWordAssoc[k].Actions[v].ActionCount = curTopic.wordCount[nWordIndex];
                        v++;                       
                    }
                    
                    topicWordAssoc[k].Prob = 0;
                    foreach (var key in docTopicAssoc.Keys)
                    {
                        if(docTopicAssoc[key].Keys.Contains(curTopic.DisplayName))
                            topicWordAssoc[k].Prob++;
                    }
                    topicWordAssoc[k].Prob /= _M;
                    Array.Sort(topicWordAssoc[k].Actions);
                }
             
                if (updater != null)
                {
                    updater.Complete();
                }
            }
            catch (Exception ex)
            {
                if (updater != null)
                {
                    if (updater.State == UpdateState.Cancelled)
                        return;
                    else
                        updater.Fail(ex.Message);
                }
                else
                    throw ex;

                return;
            }
        }
    }
}
